#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <syslog.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>
#include<poll.h>
#include "ourhdr.h"

/***
 * 为路径名动态地分配空间
 **/

#ifdef PATH_MAX
static int pathmax = PATH_MAX;
#else
static int pathmax = 0;
#endif

#define PATH_MAX_GUESS 1024

char * path_alloc( int *size)
{
    char *ptr;
    if(pathmax == 0) 
    {
        errno = 0;
        if(( pathmax = pathconf("/", _PC_PATH_MAX)) <0)
        {
            if(errno == 0)
            {
                pathmax = PATH_MAX_GUESS;
            }
            else
            {
                err_sys("pathconf error for _PC_PATH_MAX");
            }
        }
        else
        {
            pathmax++;
        }

    }
    if( (ptr = malloc(pathmax +1)) ==NULL)
    {
        err_sys("malloc error for pathname");
    }
    if(size != NULL)
    {
        *size = pathmax +1;
    }
    return ptr;
}


/***
 * 确定文件描述符数
 **/

#ifdef OPEN_MAX
static int openmax = OPEN_MAX;
#else
static int openmax =0;
#endif

#define OPEN_MAX_GUESS 256

int open_max(void)
{
    if(openmax == 0)
    {
        errno=0;
        if( (openmax = sysconf(_SC_OPEN_MAX)) < 0)
        {
            if(errno == 0)
            {
                openmax = OPEN_MAX_GUESS;
            }
            else
            {
                err_sys("sysconf error for _SC_OPEN_MAX");
            }
        }

    }
    return (openmax);
}


void clr_fl(int fd,int flags)
{

}

/***
 * 对一个文件描述符打开一个名多个文件状态标志
 **/
void set_fl(int fd,int flags)
{
    int val;
    if((val = fcntl(fd, F_GETFL,0))<0)
    {
        err_sys("fcntl F_GETL error");
    }
    val |= flags;
    if(fcntl(fd,F_SETFL, val)<0)
    {
        err_sys("fcntl F_SETFL error");
    }
}

void pr_exit(int status)
{
    //正常返回的状态，则为真，取子进程传送给exit或
    if(WIFEXITED(status))
    {
        printf("normal termination , exit status = %d\n",
            WIFEXITED(status));
    }
    else if(WIFSIGNALED(status))
    {
        printf("abnormal termination, signal nmber= %d%s\n",
            WTERMSIG(status),
#ifdef WCOREDUMP
            WCOREDUMP(status) ? "(core file.generated)" : "");
#else
            "");
#endif
    }
    else if(WIFSTOPPED(status))
    {
        printf("child stopped, signal number %d\n",
            WSTOPSIG(status));
    }
}

void pr_mask(const char *str)
{
    sigset_t sigset;
    int errno_save;
    errno_save = errno;
    if(sigprocmask(0,NULL, &sigset) <0)
    {
        err_sys("sigprocmask error");
    }
    printf("%s", str);
    if(sigismember(&sigset, SIGINT))
    {
        printf("SIGINT ");
    }
    if(sigismember(&sigset, SIGQUIT))
    {
        printf("SIGQUIT ");
    }
    if(sigismember(&sigset, SIGUSR1))
    {
        printf("SIGUSR1 ");
    }
    if(sigismember(&sigset, SIGALRM))
    {
        printf("SIGALRM ");
    }
    printf("\n");
    errno = errno_save;
}

Sigfunc* signal(int signo, Sigfunc *func)
{
    struct sigaction act, oact;
    
    //以下代码适配macOS
#ifdef _DARWIN_C_SOURCE
    act.__sigaction_u.__sa_handler = func;
#else
    act.sa_handler = func;
#endif
    sigemptyset(&act.sa_mask);
    act.sa_flags =0;
    if(signo == SIGALRM)
    {
#ifdef SA_INTERRUPT
        act.sa_flags |= SA_INTERRUPT;
#endif
    }
    else
    {
#ifdef SA_RESTART
        act.sa_flags |= SA_RESTART;
#endif
    }
    if(sigaction(signo, &act, &oact) <0)
    {
        return (SIG_ERR);
    }
#ifdef _DARWIN_C_SOURCE
    return (oact.__sigaction_u.__sa_handler);
#else
    return (oact.sa_handler);
#endif
}

Sigfunc* signal_intr(int signo, Sigfunc *func)
{
    struct sigaction act,oact;
#ifdef _DARWIN_C_SOURCE
    act.__sigaction_u.__sa_handler = func;
#else
    act.sa_handler = func;
#endif
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
#ifdef SA_INTERRUPT
    act.sa_flags |= SA_INTERRUPT;
#endif
    if(sigaction(signo, &act, &oact)<0)
    {
        return (SIG_ERR);
    }
#ifdef _DARWIN_C_SOURCE
    return (act.__sigaction_u.__sa_handler);
#else
    return (act.sa_handler);
#endif
}

static struct termios save_termios;

static int ttysavefd=-1;
static enum { RESET, RAW, CBREAK} ttystate= RESET;

int tty_cbreak(int fd)
{
    struct  termios buf;
    if(tcgetattr(fd, &save_termios)<0)
    {
        return (-1);
    }
    buf = save_termios;
    buf.c_lflag &= ~(ECHO | ICANON);
    buf.c_cc[VMIN] =1;
    buf.c_cc[VTIME] = 0;

    if(tcsetattr(fd, TCSAFLUSH, &buf)<0)
    {
        return (-1);
    }
    ttystate = CBREAK;
    ttysavefd = fd;
    return (0);
}
int tty_raw(int fd)
{
    struct  termios buf;
    if(tcgetattr(fd, &save_termios)<0)
    {
        return (-1);
    }
    buf = save_termios;

    buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
    buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    buf.c_cflag &= ~(CSIZE | PARENB);
    buf.c_cflag |= CS8;
    buf.c_oflag &= ~(OPOST);
    
    buf.c_cc[VMIN] =1;
    buf.c_cc[VTIME] = 0;
    if(tcsetattr(fd, TCSAFLUSH, &buf)<0)
    {
        return (-1);
    }
    ttystate = RAW;
    ttysavefd = fd;
    return (0);
}

int tty_reset(int fd)
{
    if(ttystate!=CBREAK && ttystate!=RAW)
    {
        return (0);
    }
    if(tcsetattr(fd,TCSAFLUSH, &save_termios)<0)
    {
        return (-1);
    }
    ttystate = RESET;
    return (0);
}

void tty_atexit(void)
{
    if(ttysavefd>=0)
    {
        tty_reset(ttysavefd);
    }
}

#ifdef ECHO
struct termios* tty_termios(void)
{
    return (&save_termios);
}
#endif




static void err_doit(int,const char*, va_list);

char *pname= NULL;

void err_ret(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    err_doit(1,fmt,ap);
    va_end(ap);
    return;
}

void err_sys(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    err_doit(1,fmt,ap);
    va_end(ap);
    exit(1);
}

void err_dump(const char *fmt,...)
{
    va_list ap;
    va_start(ap, fmt);
    err_doit(1,fmt,ap);
    va_end(ap);
    abort();
    exit(1);
}

void err_msg(const char *fmt,...)
{
    va_list ap;
    va_start(ap, fmt);
    err_doit(0,fmt,ap);
    va_end(ap);
    return;
}

void err_quit(const char *fmt,...)
{
    va_list ap;
    va_start(ap,fmt);
    err_doit(1,fmt,ap);
    va_end(ap);
    exit(1);
}

static void err_doit(int errnoflag, const char *fmt, va_list ap)
{
    int errno_save;
    char buf[MAXLINE];
    vsprintf(buf, fmt, ap);
    if(errnoflag)
    {
        sprintf(buf + strlen(buf), ": %s", strerror(errno_save));
    }

    strcat(buf, "\n");
    fflush(stdout);
    fputs(buf,stderr);
    fflush(NULL);
    return;
}

static int debug=1;

static void log_doit(int, int, const char *, va_list);

void log_open(const char *ident, int option, int facility)
{
    if(debug == 0)
    {
        openlog(ident, option, facility);
    }
}

void log_ret(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    log_doit(1,LOG_ERR, fmt, ap);
    va_end(ap);
    return;
}

void log_sys(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    log_doit(1, LOG_ERR, fmt, ap);
    va_end(ap);
    exit(2);
}

void log_msg(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    log_doit(0,LOG_ERR, fmt, ap);
    va_end(ap);
    return;
}

void log_quit(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    log_doit(0, LOG_ERR, fmt, ap);
    va_end(ap);
    exit(2);
}

static void log_doit(int errnoflag, int priority, const char *fmt, va_list ap)
{
    int errno_save;
    char buf[MAXLINE];

    errno_save = errno;
    vsprintf(buf, fmt, ap);
    if(errnoflag)
    {
        sprintf(buf + strlen(buf), ": %s", strerror(errno_save));
    }
    strcat(buf, "\n");
    if(debug)
    {
        fflush(stdout);
        fputs(buf, stderr);
        fflush(stderr);
    }
    else
    {
        syslog(priority, "%s", buf);
    }
    return;
}

static volatile sig_atomic_t sigflag;

static sigset_t newmask, oldmask, zeromask;

static void sig_usr(int signo)
{
    sigflag =1;
    return;
}

void TELL_WAIT(void)
{
    if(signal(SIGUSR1, sig_usr) == SIG_ERR)
    {
        err_sys("signal(SIGUSR1) error");
    }
    if(signal(SIGUSR2,sig_usr) == SIG_ERR)
    {
        err_sys("signal(SIGUSR2) error");
    }
    sigemptyset(&zeromask);
    sigemptyset(&newmask);

    sigaddset(&newmask, SIGUSR1);
    sigaddset(&newmask, SIGUSR2);

    if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
    {
        err_sys("SIG_BLOCK error");
    }
}

void TELL_PARENT(pid_t pid)
{
    kill(pid, SIGUSR2);
}

void WAIT_PARENT(void)
{
    while(sigflag == 0)
    {
        sigsuspend(&zeromask);
    }
    sigflag = 0;

    if(sigprocmask(SIG_SETMASK, &oldmask, NULL) <0)
    {
        err_sys("SIG_SETMAK error");
    }
}

void TELL_CHILD(pid_t pid)
{
    kill(pid, SIGUSR1);
}

void WAIT_CHILD(void)
{
    while(sigflag == 0)
    {
        sigsuspend(&zeromask);
    }
    sigflag=0;
    if(sigprocmask(SIG_SETMASK, &oldmask,NULL) <0)
    {
        err_sys("SIG_SETMASK error");
    }
}

/***
static int pfd1[2], pfd2[2];

void TELL_WAIT(void)
{
    if(pipe(pfd1)<0 || pipe(pfd2) < 0)
    {
        err_sys("pipe error");
    }
}

void TELL_PARENT(pid_t pid)
{
    if(write(pfd2[1],"c",1) != 1)
    {
        err_sys("write error");
    }
}

void WAIT_PARENT(void)
{
    char c;
    if(read(pfd1[0],&c,1)!=1)
    {
        err_sys("read error");
    }
    if(c!='p')
    {
        err_quit("WAIT_PARENT: incorrect data");
    }
}

void TELL_CHILD(pid_t pid)
{
    if(write(pfd1[1],"p",1) !=1)
    {
        err_sys("write error");
    }
}

void WAIT_CHILD(void)
{
    char c;
    if(read(pfd2[0],&c,1)!=1)
    {
        err_sys("read error");
    }
    if(c!='c')
    {
        err_quit("WAIT_CHILD: incorrect data");
    }
}
**/

int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)
{
    struct  flock lock;
    lock.l_type= type;
    lock.l_start= offset;
    lock.l_whence= whence;
    lock.l_len = len;
    return (fcntl(fd,cmd, &lock));
}

pid_t lock_test(int fd, int type, off_t offset, int whence, off_t len)
{
    struct flock lock;
    lock.l_type = type;
    lock.l_start = offset;
    lock.l_whence = whence;
    lock.l_len = len;
    if(fcntl(fd, F_GETLK, &lock)<0)
    {
        err_sys("fcntl error");
    }
    if(lock.l_type == F_UNLCK)
    {
        return (0);
    }
    return (lock.l_pid);
}

 void sleep_us_select(unsigned int nusecs)
 {
    struct timeval tval;
    tval.tv_sec = nusecs / 1000000;
    tval.tv_usec = nusecs % 1000000;
    select(0, NULL, NULL, NULL, &tval);
 }

 void sleep_us_poll(unsigned int nusecs)
 {
    struct pollfd dummy;
    int timeout;
    if((timeout == nusecs / 1000 )  <=0 )
    {
        timeout = 1;
    }
    poll(&dummy, 0 ,timeout);
 }

ssize_t readn(int fd, void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nread;
    char *ptr;
    ptr = vptr;
    nleft = n;
    while(nleft >0) 
    {
        if((nread = read(fd,ptr, nleft))<0)
        {
            return (nread);
        }
        else if(nread ==0)
        {
            break;
        }
        nleft -= nread;
        ptr += nread;
    }
    return (n - nleft);
}


 ssize_t writen(int fd, const void * vptr, size_t n)
 {
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;
    ptr = vptr;
    nleft = n;
    while(nleft  > 0)
    {
        if( (nwritten = write(fd, ptr, nleft))<=0 )
        {
            return (nwritten);
        }
        nleft -= nwritten;
        ptr += nwritten;
    }

    return (n);
 }

 int daemon_init()
{
    pid_t pid;
    if((pid =fork()) < 0)
    {
        return (-1);
    }
    else if(pid!=0)
    {
        exit(0); /** parent goes bye- bye */
    }
    setsid();   /**become session leader */
    chdir("/"); /**chagne working directory */
    umask(0);      /** clear our file mode createion mask */
    return (0);
}



static pid_t *childpid = NULL;

static int maxfd;

#define SHELL "/bin/sh"

FILE* popen(const char *cmdstring, const char *type)
{
    int i, pfd[2];
    pid_t pid;
    FILE *fp;
    if((type[0]!='r' && type[0]!='w') || type[1] != 0)
    {
        errno = EINVAL;
        return (NULL);
    }

    if(childpid == NULL)
    {
        maxfd = open_max();
        if((childpid = calloc(maxfd, sizeof(pid_t))) == NULL)
        {
            return (NULL);
        }
    }

    if(pipe(pfd) < 0)
    {
        return (NULL);
    }

    if ((pid =fork()) < 0)
    {
        return (NULL);
    }
    else if(pid  == 0) /** child **/
    {
        if(*type == 'r')
        {
            close(pfd[0]);
            if(pfd[1]!= STDOUT_FILENO)
            {
                dup2(pfd[1], STDOUT_FILENO);
                close(pfd[1]);
            }
        }
        else
        {
            close(pfd[1]);
            if(pfd[0]!= STDIN_FILENO)
            {
                dup2(pfd[0], STDIN_FILENO);
                close(pfd[0]);
            }

        }
        for(i=0;i<maxfd;i++)
        {
            if(childpid[i] >0)
            {
                close(i);
            }
        }
        execl(SHELL, "sh", "-c", cmdstring, (char *)0);
        _exit(127);
    }
    /** parent **/
    if(*type == 'r')
    {
        close(pfd[1]);
        if((fp = fdopen(pfd[0], type)) == NULL)
        {
            return (NULL);
        }
    }
    else
    {
        close(pfd[0]);
        if((fp = fdopen(pfd[1], type)) == NULL)
        {
            return (NULL);
        }
    }
    childpid[fileno(fp)] = pid;
    return (fp);
}

int pclose (FILE *fp)
{
    int fd, stat;
    pid_t pid;
    if(childpid == NULL)
    {
        return (-1);
    }
    fd = fileno(fp);
    if((pid=childpid[fd])== 0)
    {
        return (-1);
    }

    childpid[fd] = 0;
    if(fclose(fp) == EOF)
    {
        return (-1);
    }

    while(waitpid(pid, &stat, 0) < 0)
    {
        if(errno != EINTR)
        {
            return (-1);
        }
    }
    return (stat);
}


int s_pipe(int fd[2]){
    return (socketpair(AF_UNIX, SOCK_STREAM, 0, fd));
}